/**
 * 2010(c) www.jcaramel.com
 * jCARAMEL CORE
 * 
 * Requires:
 * 	jQuery 1.4.2 or higher
 */

/**
 * Adds the forEach function to the array prototype
 * IE doesn't support Array.forEach
 * @param {Object} fun
 */
if (!Array.prototype.forEach){
  Array.prototype.forEach = function(fun){
	var len = this.length >>> 0;
	if (typeof fun != "function")
	  throw new TypeError();

	var thisp = arguments[1];
	for (var i = 0; i < len; i++){
	  if (i in this)
		fun.call(thisp, this[i], i, this);
	}
  };
}

LSV_DEFAULT_COLUMN_WIDTH = 200;

//Global jCaramel Object
var jCaramel = {
	
	
	
	/**
	 * Keeps track of event bindings
	 */
	eventBindings: {},
	
	/**
	 * Keeps track of jCaramel elements
	 */
	handles: 0,
	
	/**
	 * Right to left orientation
	 */
	rtl: false,
	
	
	/**
	 * Binds a handler to the first element
	 * @param {jQuery} element jCaramel element to assign the event handler
	 * @param {string} eventName Event name
	 * @param {function} handler Handler to execute when event fires
	 */
	bind: function(element, eventName, handler){
	
		if( !element.tag('handle') )
			throw "Element is not a jCaramel Object";
		
		var prop = element.tag('handle') + eventName;
		
		if(!jCaramel.eventBindings[prop])
			jCaramel.eventBindings[prop] = [];
			
		jCaramel.eventBindings[prop].push(handler);
		
	},
	
	/**
	 * Ensures that the element or elements passed have a jCaramel handle
	 * @param {jQuery} element
	 */
	createHandle: function(element){
		
		if(element.size() > 1)
			element.each(function(){ jCaramel.createHandle(jQuery(this)); });
		
		if(!element.tag('handle')){
			element.tag('handle', ++jCaramel.handles)	
		}
		
	},
	
	/**
	* Scrolls the container if possible/necessary to show element
	*/
	ensureVisible: function(element, container, shadowTop){
		
		if(!shadowTop) shadowTop = 0;
		
		//Check scroll down
		if( (element.position().top + element.outerHeight()) > container.innerHeight()){
			container.scrollTop((element.position().top - container.innerHeight()));
		} 
		
		//Check scroll up
		if( element.position().top < shadowTop  ){
			container.scrollTop(element.position().top + shadowTop);
		}
	},
	
	/**
	 * Returns the trigger (function) for the specified event
	 * @param {jQuery} element Element to check for trigger
	 * @param {string} eventName Name of the event to retrieve
	 * @return {function} Function to be executed
	 */
	fire: function(element, eventName, data){
		if( !element.tag('handle') )
			throw "Element is not a jCaramel Object";
		
		var prop = element.tag('handle') + eventName;
		
		if(jCaramel.eventBindings[prop])
			jCaramel.eventBindings[prop].forEach(function(f){
				if (f) {
					f.apply(element, data);
				}
			});
	},
	
	
	
	/**
	 * Gets an jQuery object from the specified handle
	 * @param {string} handle
	 * @return {jQuery} Selected object(s)
	 */
	getByHandle: function(handle){
		return jQuery('*[jcaramel-handle="' + handle + '"]');
	},
	
	/**
	 * Unbinds handlers to the first element
	 * @param {jQuery} element jCaramel element
	 * @param {string} eventName Name of the event to unbind
	 */
	unbind: function(element, eventName){
		
		if( !element.tag('handle') )
			throw "Element is not a jCaramel Object";
		
		var prop = element.tag('handle') + eventName;
		
		if(jCaramel.eventBindings[prop])
			jCaramel.eventBindings[prop] = [];
		
	}
	
}//End jCaramel namespace;

/**
 * Gets or sets a tag value for the element
 * @param {Object} tag
 * @param {Object} value
 */
jQuery.fn.tag = function(tag, value){
	
	if(typeof(value) != 'undefined'){
		this.attr('jcaramel-' + tag, value);
	}else{
		var val = this.attr('jcaramel-' + tag); 
		return val ? val : false;
	}
	
	return this;
};

/**
 * Gets or sets a flag for the element
 * @param {Object} tag
 * @param {Object} value
 */
jQuery.fn.flag = function(flag, value){
	
	if(typeof(value) != 'undefined'){
		this.attr('jcaramel-' + flag,( value ? "1": "0"));
	}else{
		var v = this.attr('jcaramel-' + flag);
		return v == "1" ? true : false; //Hardly typed :s
	}
	
	return this;
	
};

